

package com.yungu.swift.schedule.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.yungu.swift.assets.driver.model.dto.DriverDto;
import com.yungu.swift.assets.driver.service.DriverExamineService;
import com.yungu.swift.assets.driver.service.DriverService;
import com.yungu.swift.autoconfig.redis.RedisCacheService;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.common.service.CommonSmsService;
import com.yungu.swift.constants.MagicConstant;
import com.yungu.swift.lbs.service.LocationService;
import com.yungu.swift.schedule.config.ApplicationConfig;
import com.yungu.swift.utils.JsonUtils;
import com.yungu.swift.utils.ParamUtil;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Zerrion
 * @version 1.0
 * @description 扫描出车司机点任务
 * @date 2017/9/19 10:59
 */
@Slf4j
@Service
public class ScanDriverPointService {

    /**
     * 5分钟的毫秒数、30分钟的毫秒数、60分钟的毫秒数
     */
    private static final long FIVE_MINUTE_TIME = 1000 * 60 * 5;
    private static final long THIRTY_MINUTE_TIME = 1000 * 60 * 30;
    private static final long SIXTY_MINUTE_TIME = 1000 * 60 * 60;

    /**
     * 存取自动下线司机（离线60分钟司机）
     */
    private List<String> offWorkList = new ArrayList<>();
    /**
     * 存取离线30分钟司机
     */
    private List<String> thirtyList = new ArrayList<>();
    /**
     * 存取离线5分钟司机
     */
    private List<String> fiveList = new ArrayList<>();

    private static final ExecutorService POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1, new ThreadFactory() {
        AtomicInteger atomicInteger = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "scan-driver-point-" + atomicInteger.incrementAndGet());
        }
    });

    @Autowired
    private RedisCacheService redisCacheService;
    @Reference
    private CommonSmsService commonSmsService;
    @Reference
    private DriverService driverService;
    @Reference
    private LocationService locationService;
    @Reference
    private DriverExamineService driverExamineService;

    public void insertWorkTime() {
        log.info("------------扫描出车司机点任务开始------------");
        fiveList.clear();
        thirtyList.clear();
        offWorkList.clear();
        Object object = redisCacheService.get(MagicConstant.PARAMS_ONWORK_DRIVER, false);
        if (object == null) {
            log.info("无出车司机---------扫描出车司机点任务结束------------");
            return;
        }
        //遍历出车司机
        for (String driverUuid : (Set<String>) object) {
            Object driverUpload = redisCacheService.get("driverUpload" + driverUuid, false);
            if (driverUpload == null) {
                log.warn("司机：{} 无最后上传点时间", driverUuid);
                continue;
            }
            //取出每个司机的最后传点时间
            Date uploadDate = (Date) driverUpload;
            //记录离在线状态
            driverService.recordOnOrOffLine(driverUuid, uploadDate.getTime());
            long timeDiff = System.currentTimeMillis() - uploadDate.getTime();
            if (timeDiff >= SIXTY_MINUTE_TIME) {
                offWorkList.add(driverUuid);
            } else if (timeDiff >= THIRTY_MINUTE_TIME) {
                thirtyList.add(driverUuid);
            } else if (timeDiff >= FIVE_MINUTE_TIME) {
                fiveList.add(driverUuid);
            } else {

            }
        }
        log.info("已被系统自动下线司机：" + offWorkList.toString());
        log.info("距离30分钟自动下线司机：" + thirtyList.toString());
        log.info("距离55分钟自动下线司机：" + fiveList.toString());
        POOL.execute(new SendMsgRunner());
        log.info("------------扫描出车司机点任务结束------------");
    }

    /**
     * 短信发送线程
     */
    class SendMsgRunner implements Runnable {

        @Override
        public void run() {
            boolean msgAlarm = ApplicationConfig.SERVER_CONFIG.getSystem().getSendMsgAlarm();
            // 1. 60分钟无上传点的司机自动收车
            if (CollectionUtils.isNotEmpty(offWorkList)) {
                for (String driverUuid : offWorkList) {
                    //这边要判断下是否有订单，如果有订单就不做下线操作
                    String orderUuid = (String) redisCacheService.get("hasOrder" + driverUuid, false);
                    if (StringUtils.isNotEmpty(orderUuid)) {
                        log.info("司机：{} 已离线，但身上还有订单，不做收车动作", driverUuid);
                        continue;
                    }
                    Object lng = ParamUtil.defaultIfNull(redisCacheService.get(MagicConstant.PARAMS_DRIVER_CURRENT_LNG + driverUuid, false), 0.0D);
                    Object lat = ParamUtil.defaultIfNull(redisCacheService.get(MagicConstant.PARAMS_DRIVER_CURRENT_LAT + driverUuid, false), 0.0D);
                    driverService.offWork(driverUuid, (Double) lng, (Double) lat, "");
                    log.info("司机：{} 已被系统自动收车---", driverUuid);
                    //这边复制最后一个点，作为下班点
                    locationService.copyLastPointAsOffWorkPoint(driverUuid);
                    // 发送收车短信
                    if (msgAlarm) {
                        sendMsgToDriver(driverUuid, "师傅，您好，您的车辆在出车中，但是服务器已连续60分钟接收不到定位信息，您可能已离线，系统已为您自动收车。");
                    }
                }
            }
            // 2. 离线5分钟、30分钟根据系统要求是否选择是否发送短信
            if (!msgAlarm) {
                return;
            }
            if (CollectionUtils.isNotEmpty(thirtyList)) {
                for (String driverUuid : thirtyList) {
                    sendMsgToDriver(driverUuid, "师傅，您好，您的车辆在出车中，但是服务器已连续30分钟接收不到定位信息，您可能已离线，请进入应用，检查网络和GPS定位。30分钟内未处理系统将自动收车。");
                }
            }
            if (CollectionUtils.isNotEmpty(fiveList)) {
                for (String driverUuid : fiveList) {
                    sendMsgToDriver(driverUuid, "师傅，您好，您的车辆在出车中，但是服务器已连续5分钟接收不到定位信息，您可能已离线，请进入应用，检查网络和GPS定位。30分钟内未处理系统将自动收车。");
                }
            }
        }
    }


    /**
     * 从缓存获取司机标识，如果该标识下未发送提醒短信则发短信提醒
     *
     * @param driverUuid
     * @param content    短信内容
     */
    private void sendMsgToDriver(String driverUuid, String content) {
        // 1小时只允许发送一条
        String alramFalg = "driver_alarm_flag_" + driverUuid;
        Object flag = redisCacheService.get(alramFalg);
        if (flag != null) {
            log.info("司机：{} 一小时内已经发送过提醒短信", driverUuid);
            return;
        }
        try {
            Map<String, String> driverMap = new HashMap<>();
            Object driverInfo = redisCacheService.get(driverUuid);
            if (driverInfo != null) {
                driverMap = JsonUtils.readValue(driverInfo.toString(), Map.class);
            } else {
                DriverDto driverDto = driverService.get(driverUuid).getData();
                if (driverDto == null) {
                    log.info("司机：{} 获取不到司机信息", driverUuid);
                    return;
                }
                driverMap.put("name", driverDto.getName());
                driverMap.put("mobile", driverDto.getMobile());
                driverMap.put("appid", driverDto.getAppid());
                redisCacheService.set(driverUuid, JsonUtils.writeJsonStr(driverMap), 30L);
            }
            commonSmsService.sendCommon(driverMap.get("mobile"), driverMap.get("name") + content, driverMap.get("appid"));
            redisCacheService.set(alramFalg, true, 60L);
        } catch (IOException e) {
            log.error("ScanDriverPointService sendMsgToDriver JsonUtils readValue error", e);
        }
    }


    /**
     * 司机考核（每天2点）
     */
    public void examine() {
        log.info("---------司机考核任务开始---------");
        ResponseData<Boolean> responseData = driverExamineService.examine();
        log.info("---------司机考核任务结束-----{}", responseData.getMsg());
    }

    /**
     * 分割空驶跨天状态定时任务（每天0点）
     */
    public void statusSplit() {
        log.info("---------扫描处理空驶跨天司机状态开始---------");
        driverService.statusSplit();
        log.info("---------扫描处理空驶跨天司机状态结束---------");
    }

}
